package com.se.invigilation.service;

import com.se.invigilation.dox.InviDetail;
import com.se.invigilation.dox.Invigilation;
import com.se.invigilation.dox.User;
import com.se.invigilation.dto.AssignUserDTO;
import com.se.invigilation.dto.CancelNoticeDTO;
import com.se.invigilation.repository.InviDetailRepository;
import com.se.invigilation.repository.InvigilationRepository;
import com.se.invigilation.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

@Service
@Slf4j
@RequiredArgsConstructor
public class CommonService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final DingtalkService dingtalkService;
    private final InvigilationRepository invigilationRepository;
    private final InviDetailRepository inviDetailRepository;

    public Mono<User> getUser(String account) {
        return userRepository.findByAccount(account);
    }

    @Transactional
    public Mono<Integer> updatePassword(String uid, String password) {
        return userRepository.updatePasswordById(uid, this.passwordEncoder.encode(password));
    }

    public Mono<List<User>> listUsersDingIds(List<String> ids) {
        return userRepository.findDingIdByIds(ids)
                .collectList();
    }

    public Mono<Void> sendCancelNotice(CancelNoticeDTO cancelNotice) {
        var dingNotice = cancelNotice.getDingNotice();
        Mono<String> sendNoM = dingtalkService.sendNotice(dingNotice.getDingIds(), cancelNotice.getMessage());
        Mono<List<String>> listMono = Flux.fromIterable(dingNotice.getCalendars())
                .flatMap(calendar -> dingtalkService.deleteCalendar(calendar.getUnionId(), calendar.getEventId()))
                .collectList();
        return Mono.when(sendNoM, listMono);
    }

    // 获取监考用户
    public Mono<List<User>> listUserDingIdsByInviid(String inviid) {
        return userRepository.findByInviId(inviid).collectList();
    }

    @Transactional
    public Mono<Integer> addInvigilation(Invigilation invigilation) {
        return invigilationRepository.save(invigilation).thenReturn(1);
    }

    @Transactional
    public Mono<Void> updateInviDingNotice(String inviid, String dingNotice) {
        return invigilationRepository.updateDingNotice(inviid, dingNotice).then();
    }

    @Transactional
    public Mono<Invigilation> addInvidetails(String inviid, AssignUserDTO assignUser) {
        // 移除原详细分配
        Mono<Integer> delInviDetailsM = inviDetailRepository.deleteByInviId(inviid);
        // 创建新详细分配
        Mono<Void> DetailM = Flux.fromIterable(assignUser.getUserIds())
                .flatMap(uid -> {
                    InviDetail d = InviDetail.builder()
                            .inviId(inviid)
                            .userId(uid)
                            .build();
                    return inviDetailRepository.save(d);
                }).then();

        // 更新监考信息
        Mono<Invigilation> updateInviM = invigilationRepository.findById(inviid)
                .flatMap(invi -> {
                    if (StringUtils.hasLength(assignUser.getDepartment())) {
                        invi.setDepartment(assignUser.getDepartment());
                    }
                    if (assignUser.getAmount() != null) {
                        invi.setAmount(assignUser.getAmount());
                    }
                    if (StringUtils.hasLength(assignUser.getDispatcher())) {
                        invi.setDispatcher(assignUser.getDispatcher());
                    }
                    if (StringUtils.hasLength(assignUser.getExecutor())) {
                        invi.setExecutor(assignUser.getExecutor());
                    }
                    if (StringUtils.hasLength(assignUser.getAllocator())) {
                        invi.setAllocator(assignUser.getAllocator());
                    }
                    invi.setStatus(Invigilation.ASSIGN);
                    return invigilationRepository.save(invi);
                });
        return delInviDetailsM.then(DetailM)
                .then(updateInviM);
    }
}
